package rpc.server.netty;

import com.alibaba.nacos.api.exception.NacosException;
import io.netty.handler.codec.LengthFieldBasedFrameDecoder;
import io.netty.handler.timeout.IdleState;
import io.netty.handler.timeout.IdleStateEvent;
import io.netty.handler.timeout.IdleStateHandler;
import rpc.common.annotation.Service;
import rpc.common.annotation.ServiceScan;
import rpc.common.enumeraion.RpcError;
import rpc.common.exception.RpcException;
import rpc.common.utils.NacosUtil;
import rpc.common.utils.ServiceScanner;
import rpc.common.utils.ShutdownHook;
import rpc.intf.RpcServer;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.logging.LogLevel;
import io.netty.handler.logging.LoggingHandler;
import lombok.extern.slf4j.Slf4j;
import rpc.intf.impl.serializer.KryoSerializer;
import rpc.packet.codec.CommonCodec;
import rpc.packet.codec.CommonDecoder;
import rpc.packet.codec.CommonEncoder;

import java.net.InetSocketAddress;
import java.util.Set;

@Slf4j
public class NettyServer implements RpcServer {

    private String host;
    private int port;
    //private NacosServiceRegistry serviceRegistry;
    //private

    public NettyServer(String host, int port) {
        this.host = host;
        this.port = port;
        //serviceRegistry = new NacosServiceRegistry();
        scanService();
    }

    /**
     * 扫描服务
     */
    private void scanService() {
        String mainClassName = ServiceScanner.getMainName();
        Class<?> startClass;
        try {
            startClass = Class.forName(mainClassName);
            if (!startClass.isAnnotationPresent(ServiceScan.class)) {
                log.error("启动类缺少 @ServiceScan 注解");
                throw new RpcException(RpcError.SERVICE_SCAN_PACKAGE_NOT_FOUND);
            }
            log.debug("启动类加载成功");
        } catch (ClassNotFoundException e) {
            log.debug("出现未知错误");//???
            throw new RpcException(RpcError.UNKNOWN_ERROR);
        }
        String basePackage = startClass.getAnnotation(ServiceScan.class).value();
        if("".equals(basePackage)) {
            //进入默认设置
            //默认时：服务接口实现应放于main类同级目录下
            basePackage = mainClassName.substring(0, mainClassName.lastIndexOf("."));
        }
        log.debug("basePackage: {}", basePackage);
        //获取目录下添加了Service注解的class文件
        Set<Class<?>> classSet = ServiceScanner.getClassesByPath(basePackage);
        for (Class<?> clazz : classSet) {
            if (clazz.isAnnotationPresent(Service.class)) {
                String serviceName = clazz.getAnnotation(Service.class).name();
                Object obj;
                try {
                    obj = clazz.newInstance();
                } catch (InstantiationException | IllegalAccessException e) {
                    log.debug("创建 " + clazz + " 时有错误发生");
                    continue;
                }

                if ("".equals(serviceName)) {
                    //进入默认设置
                    Class<?>[] interfaces = clazz.getInterfaces();
                    for (Class<?> serviceInterface: interfaces){
                        log.debug("obj : {}", obj);
                        log.debug("oneInterface.getCanonicalName() : {}", serviceInterface.getCanonicalName());
                        publishService(obj, serviceInterface.getCanonicalName());
                    }
                } else {
                    publishService(obj, serviceName);
                }
            }
        }
    }

    /**
     *
     * @param service 实现接口的实现类
     * @param serviceName 接口名
     */
    private void publishService(Object service, String serviceName) {
        //本地注册
        NettyServerHandler.serviceProvider.register(service);
        try {
            NacosUtil.registerService(serviceName, new InetSocketAddress(host, port));
        } catch (NacosException e) {
            //e.printStackTrace();
            log.debug("注册服务时有错误发生: {}", e);
        }
    }

    @Override
    public void start() {

        EventLoopGroup bossGroup = new NioEventLoopGroup(1);//监听accept
        EventLoopGroup workerGroup = new NioEventLoopGroup();//监听链接
        try {
            ServerBootstrap serverBootstrap = new ServerBootstrap();
            //配置设置
            serverBootstrap.group(bossGroup, workerGroup)
                    //设置服务通道实现类型
                    .channel(NioServerSocketChannel.class)
                    .handler(new LoggingHandler((LogLevel.DEBUG)))
                    //给ServerSocketChannel配置参数
                    //设置全连接对列大小，既接收的accept连接數量
                    .option(ChannelOption.SO_BACKLOG, 256)
                    //给SocketChannel配置参数
                    //设置为true时，无延迟发送。即不开启nagle，消息及时发送
                    .childOption(ChannelOption.TCP_NODELAY, true)
                    .childOption(ChannelOption.SO_KEEPALIVE, true)
                    .childHandler(new ChannelInitializer<NioSocketChannel>() {
                        @Override
                        protected void initChannel(NioSocketChannel ch) throws Exception {
                            ChannelPipeline pipeline = ch.pipeline();
                            //处理连接假死 用来判断是否读时间或者写时间过长
                            //5秒内没有收到读事件会触发一个 IdleState#READER_IDLE事件
                            pipeline.addLast(new IdleStateHandler(5, 0, 0));
                            //入站出站处理器
                            pipeline.addLast(new ChannelDuplexHandler() {
                                //用来触发特殊事件
                                @Override
                                public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
                                    IdleStateEvent event = (IdleStateEvent) evt;
                                    if (event.state() == IdleState.READER_IDLE) {
                                        log.debug("已经 5s 没有读到该channel数据了");
                                    }
                                    super.userEventTriggered(ctx, evt);
                                }
                            });
                            pipeline.addLast(new LoggingHandler(LogLevel.DEBUG));
                            //粘包 半包处理
                            pipeline.addLast(new LengthFieldBasedFrameDecoder(
                                    1024, 7, 4, 0, 0));
                            pipeline.addLast(new CommonCodec());
                            pipeline.addLast(new NettyServerHandler());
                        }
                    });
            //对关闭通道进行监听
            //1
            //ChannelFuture future = serverBootstrap.bind(port).sync()//同步
            //2
            ChannelFuture future = serverBootstrap.bind(port).addListener(promise -> {
                log.debug("bind port successfully");
            });//同步
            //加载钩子
            //好像nacos会自动删除挂了的服务器？？？
            ShutdownHook.getShutdownHook().addClearAllHook();
            future.channel().closeFuture().sync();
        } catch (InterruptedException e) {
            log.debug("启动服务器时有错误发生: {}", e);
        } finally {
            log.debug("优雅关闭");
            bossGroup.shutdownGracefully();
            workerGroup.shutdownGracefully();
        }
    }

    @Override
    public <T> void publishService(Object service, Class<T> serviceClass) {
        //本地注册
//        NettyServerHandler.serviceProvider.register(service);
//        try {
//            NacosUtil.registerService(serviceClass.getCanonicalName(), new InetSocketAddress(host, port));
//        } catch (NacosException e) {
//            //e.printStackTrace();
//            log.debug("注册服务时有错误发生: {}", e);
//        }
    }


}
